require File.join(File.dirname(__FILE__), 'task')

class Array
  def to_batches(batch_size)
    groups = []
    while !empty?
      groups << slice!(0, batch_size)
    end
    groups
  end

  def shuffle
    sort_by {rand}
  end
end

module BulkMail
  # Sender instances perform the actual work of sending an email. Each sender 
  # sets up a thread pool (by default with up to 20 threads) for performing the
  # work. When creating a Sender instance, a hash of options is supplied:
  # 
  #   BulkMail::Sender.new({
  #     :list => addresses,
  #     :from => my_address,
  #     :helo => my_helo_domain,
  #     :concurrent => 50, # size of thread pool
  #     :message => message
  #   })
  #
  # To start sending, invoke Sender#start. The start method will block until
  # all addresses have been processed.
  class Sender
    # Initializes a new instance with the supplied options hash, and creates a
    # thread pool (with a default maximum size of 20).
    def initialize(opts)
      @opts = opts
      @pool = ThreadPool.new(@opts[:concurrent] || 20)
      @smtp_servers = {}
      @mutex = Mutex.new
    end
    
    # Starts processing the addresses by organizing them by domain, then
    # the
    def start
      puts "Sending to #{@opts[:list].size} addresses..."
      tasks = []
      by_domain = BulkMail.addresses_by_domain(@opts[:list])
      by_domain.each do |domain, addrs|
        addrs.to_batches(20).inject(tasks) {|m, g| m << Task.new(domain, g, @opts)}
      end
      # tasks.shuffle.each {|t| @pool.process {t.run}}
      tasks.shuffle.each do |t|
        while Thread.list.size > 20
          sleep 1
        end
        t.run
      end
      @pool.join
    rescue => e
      puts e.message
      puts e.backtrace.join("\r\n")
    ensure
      puts "Sent to #{Task.sent_count} of #{@opts[:list].size} addresses..."
    end
  end
end
